#include "inet.h"
#include "rda5981_sniffer.h"
#include "Airkiss.h"
#include "AirkissInterface.h"
#include "rda_wdt_api.h"

#define AIRKISS_LOG_LEVEL 3

#if defined(AIRKISS_LOG_LEVEL)
#define AIRKISS_LOG(_level, ...)     \
    printf("[%s][%lu] %s(%d): ", _level ,us_ticker_read(), __FILE__, __LINE__), \
    printf(__VA_ARGS__), \
    printf("\n");
#endif

#if defined(AIRKISS_LOG_LEVEL) && (AIRKISS_LOG_LEVEL >= 5)
#define AIRKISS_LOGV(...)          AIRKISS_LOG("V", __VA_ARGS__)
#else
#define AIRKISS_LOGV(...)
#endif

#if defined(AIRKISS_LOG_LEVEL) && (AIRKISS_LOG_LEVEL >= 4)
#define AIRKISS_LOGD(...)          AIRKISS_LOG("D", __VA_ARGS__)
#else
#define AIRKISS_LOGD(...)
#endif

#if defined(AIRKISS_LOG_LEVEL) && (AIRKISS_LOG_LEVEL >= 3)
#define AIRKISS_LOGI(...)          AIRKISS_LOG("I", __VA_ARGS__)
#else
#define AIRKISS_LOGI(...)
#endif

#if defined(AIRKISS_LOG_LEVEL) && (AIRKISS_LOG_LEVEL >= 2)
#define AIRKISS_LOGW(...)          AIRKISS_LOG("W", __VA_ARGS__)
#else
#define AIRKISS_LOGW(...)
#endif

#if defined(AIRKISS_LOG_LEVEL) && (AIRKISS_LOG_LEVEL >= 1)
#define AIRKISS_LOGE(...)          AIRKISS_LOG("E", __VA_ARGS__)
#else
#define AIRKISS_LOGE(...)
#endif


static void AirkissChangeChannel(void const *pArg);
static void AirkissWifiRX(uint8_t *buf, uint16_t len);
static void AirkissFinish();
static int AirkissHandler(unsigned short data_len, void *data);
static int AirkissSendRsp(uint8_t random);
static void AirkissScanChannel();

rtos::Semaphore gAirkissSem(0);
WiFiStackInterface * gWiFiStackInterface = NULL;
Airkiss::IOnEvent * gEvent = NULL;
rtos::RtosTimer gTimer(AirkissChangeChannel,osTimerPeriodic,0);

Airkiss Airkiss::mInstance;
int nAirkiss = 0;
airkiss_result_t result;
uint8_t uScanChannel[15];
int cur_channel = 0;
int to_ds = 1;
int from_ds = 0;
int mgm_frame = 0;


static const airkiss_config_t akconf =
{
	(airkiss_memset_fn)&memset,
	(airkiss_memcpy_fn)&memcpy,
	(airkiss_memcmp_fn)&memcmp,
	(airkiss_printf_fn)&printf
};
airkiss_context_t akcontex;


Airkiss & Airkiss::Instance()
{
	return mInstance;
}


Airkiss::Airkiss()
{
		
}

Airkiss::~Airkiss()
{
	
}

void Airkiss::SetEventListener(IOnEvent* _pEvent)
{
	gEvent = _pEvent;	
}


void Airkiss::Init(WiFiStackInterface * pWiFiStackInterface)
{
	gWiFiStackInterface = pWiFiStackInterface;	
	AirkissScanChannel();

}

void Airkiss::AirkissStop()
{
	if(nAirkiss)
	{
		rda5981_stop_sniffer();
		rda5981_disable_sniffer();
		gTimer.stop();
		nAirkiss = 0;
	}
}

void Airkiss::AirkissStart()
{
	AIRKISS_LOGI("AirkissStart");
	
	
	if(nAirkiss)
	{
		AIRKISS_LOGI("Airkissing so exit");
		gEvent->on_airkiss_wait();
		return;
	}
	AirkissStop();

	gWiFiStackInterface->disconnect();
	rda5981_enable_sniffer(AirkissHandler);	
	airkiss_init(&akcontex, &akconf);
	gEvent->on_airkiss_wait();
	gTimer.start(200);
	nAirkiss = 1;
}

void Airkiss::AirkissFinish()
{
	if(nAirkiss)
	{
		AirkissSendRsp(result.random);
		nAirkiss = 0;
	}
}

bool Airkiss::IsAirkiss()
{
	return nAirkiss==1?true:false;
}

static void AirkissScanChannel()
{	
	int i;
    int ret;
    rda5981_scan_result scan_result[30];
    memset(uScanChannel, 0, sizeof(uScanChannel));
    ret = gWiFiStackInterface->scan(NULL, 0);
    ret = gWiFiStackInterface->scan_result(scan_result, 30);
    AIRKISS_LOGI("scan result:%d", ret);

    for(i=0; i<ret; ++i) 
	{
        if (scan_result[i].channel<=13) 
		{
            AIRKISS_LOGI("i:%d, channel%d", i, scan_result[i].channel);
            uScanChannel[scan_result[i].channel] = 1;
        }
    }
}

static int AirkissSendRsp(uint8_t random)
{
    int i;
    int ret;
    int udp_broadcast = 1;
    UDPSocket udp_socket;
    ip_addr_t ip4_addr;
    char host_ip_addr[16];

    memset((u8_t *)(&ip4_addr), 0xff, 4);
    strcpy(host_ip_addr, inet_ntoa(ip4_addr));

    AIRKISS_LOGI("send response to host:%s", host_ip_addr);

    ret = udp_socket.open(gWiFiStackInterface);
    if (ret) {
        AIRKISS_LOGI("open socket error:%d", ret);
        return ret;
    }

    ret = udp_socket.setsockopt(0,NSAPI_UDP_BROADCAST,&udp_broadcast,sizeof(udp_broadcast));
    if (ret) {
        AIRKISS_LOGI("setsockopt error:%d", ret);
        return ret;
    }

    for (i=0; i<30; ++i) {
        ret = udp_socket.sendto(host_ip_addr, 10000, &random, 1);
        if (ret <= 0)
            AIRKISS_LOGI("send rsp fail%d", ret);
        wait_us(100 * 1000);
    }
	udp_socket.close();
    AIRKISS_LOGI("send response to host done");
    return ret;
}

static void AirkissChangeChannel(void const *pArg)
{	
    int i;
    do {
        cur_channel++;
    	if (cur_channel > 13) {
    		cur_channel = 1;
            to_ds = to_ds?0:1;
            from_ds = from_ds?0:1;
    	}
    } while (uScanChannel[cur_channel] == 0);

	AIRKISS_LOGI("cur_channel = %d",cur_channel);
    rda5981_start_sniffer(cur_channel, to_ds, from_ds, mgm_frame, 0);
    airkiss_change_channel(&akcontex);
}


static void AirkissFinish()
{
	int err;
	
	err = airkiss_get_result(&akcontex, &result);
	if (err == 0)
	{
		AIRKISS_LOGI("airkiss_get_result() ok!");
        AIRKISS_LOGI("ssid = \"%s\", pwd = \"%s\", ssid_length = %d, \"pwd_length = %d, random = 0x%02x",
        result.ssid, result.pwd, result.ssid_length, result.pwd_length, result.random);
        rda5981_stop_sniffer();
       	rda5981_flash_write_sta_data(result.ssid, result.pwd);
		gEvent->on_airkiss_finish();
	}
	else
	{
		AIRKISS_LOGI("airkiss_get_result() failed !");
	}
	
}


static void AirkissWifiRX(uint8_t *buf, uint16_t len)
{
	int ret;
	AIRKISS_LOGD("len = %d",len);
	ret = airkiss_recv(&akcontex, buf, len);
	if ( ret == AIRKISS_STATUS_CHANNEL_LOCKED)
	{
		gEvent->on_airkiss_connect();
		AIRKISS_LOGW("AIRKISS_STATUS_CHANNEL_LOCKED");
		gTimer.stop();				
	}    
	else if ( ret == AIRKISS_STATUS_COMPLETE )
	{
		
		AIRKISS_LOGI("AIRKISS_STATUS_COMPLETE");
		AirkissFinish();
	}
}

static int AirkissHandler(unsigned short data_len, void *data)
{
    static unsigned short seq_num = 0, seq_tmp;
    char *frame = (char *)data;
    seq_tmp = (frame[22] | (frame[23]<<8))>>4;
	
    if (seq_tmp == seq_num) return 0;   
    else seq_num = seq_tmp;
	
    AirkissWifiRX((uint8_t*)data, data_len);
    return 0;
}

